package com.marketlive.biz.session.context;

/*
(C) Copyright MarketLive. 2006. All rights reserved.
MarketLive is a trademark of MarketLive, Inc.
Warning: This computer program is protected by copyright law and international treaties.
Unauthorized reproduction or distribution of this program, or any portion of it, may result
in severe civil and criminal penalties, and will be prosecuted to the maximum extent
possible under the law.
*/

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.marketlive.biz.account.IAccountManager;
import org.marketlive.biz.session.context.ICommerceSession;
import org.marketlive.biz.session.context.ICommerceSessionManager;
import org.marketlive.biz.session.context.ICreateSessionExtension;
import org.marketlive.biz.session.context.IEndRequestExtension;
import org.marketlive.biz.session.context.ILoginExtension;
import org.marketlive.biz.session.context.ILogoutExtension;
import org.marketlive.biz.session.context.IRegisterCustomerExtension;
import org.marketlive.biz.session.context.IStartRequestExtension;
import org.marketlive.entity.IPrimaryKey;
import org.marketlive.entity.account.ICustomerHome;
import org.marketlive.system.config.IConfigurationManager;

import java.math.BigDecimal;
import java.util.*;

import javax.servlet.http.HttpServletRequest;

/**
 * Place to go to get the CommerceSession for an HttpServletRequest.  This
 * implementation simply stores the CommerceSession in the Servlet Engine's
 * HttpSession object.
 *
 * @author Huw
 */
public class CommerceSessionManager implements ICommerceSessionManager {
	/**
	 * Logger.
	 */
	private static Log log = LogFactory.getLog(CommerceSessionManager.class);

    private static String CUSTOMER_DURATION = "biz.session.customer_duration";

	private ICustomerHome customerHome;
	private IAccountManager accountManager;
	//private IIdGenerator idGenerator;

	/**
	 * List of extensions to check when a session is created.
	 */
	private List createSessionExtensions = new ArrayList();

	/**
	 * List of extensions to check when a request is started.
	 */
	private List startRequestExtensions = new ArrayList();

	/**
	 * List of extensions to check when a request is ended.
	 */
	private List endRequestExtensions = new ArrayList();

	/**
	 * List of extensions to check when a logging in.
	 */
	private List loginExtensions = new ArrayList();

	/**
	 * List of extensions to check when a logging out.
	 */
	private List logoutExtensions = new ArrayList();

	/**
	 * List of extensions to check when registering a customer.
	 */
	private List registerCustomerExtensions = new ArrayList();

	// performs all the finalization processes for a commecre session
	private CommerceSessionFinalizer finalizer;


    private IConfigurationManager configurationManager;

	/**
	 * Creates new SessionManager
	 */
	public CommerceSessionManager() {
	}

	public void init() throws Exception {
		// set up session finalization processes
		finalizer = new CommerceSessionFinalizer();
		finalizer.registerCommand(new UpdateCustomerCommand());
	}

	public int getCustomerDuration() {
		return configurationManager.getAsInt(CUSTOMER_DURATION);
	}

	public ICustomerHome getCustomerHome() {
		return customerHome;
	}

	public void setCustomerHome(ICustomerHome customerHome) {
		this.customerHome = customerHome;
	}

	public IAccountManager getAccountManager() {
		return accountManager;
	}

	public void setAccountManager(IAccountManager accountManager) {
		this.accountManager = accountManager;
	}

	/*public IIdGenerator getIdGenerator() {
		return idGenerator;
	}

	public void setIdGenerator(IIdGenerator idGenerator) {
		this.idGenerator = idGenerator;
	}*/

    /**
     * {@inheritDoc}
     */
	public List getCreateSessionExtensions() {
		return createSessionExtensions;
	}

    /**
     * {@inheritDoc}
     */
	public List getStartRequestExtensions() {
		return startRequestExtensions;
	}

    /**
     * {@inheritDoc}
     */
	public List getEndRequestExtensions() {
		return endRequestExtensions;
	}

    /**
     * {@inheritDoc}
     */
	public List getLoginExtensions() {
		return loginExtensions;
	}

    /**
     * {@inheritDoc}
     */
	public List getLogoutExtensions() {
		return logoutExtensions;
	}

    /**
     * {@inheritDoc}
     */
	public List getRegisterCustomerExtensions() {
		return registerCustomerExtensions;
	}

    public void setConfigurationManager(IConfigurationManager configurationManager) {
        this.configurationManager = configurationManager;
    }

	/**
	 * Creates a new session
	 */
	public ICommerceSession createSession(String customerId, String basketId) {
		log.info("createSession(): customerId: " + customerId + " basketId: " + basketId);

        // create a uniqe commerceSessionID without going to the DB
        BigDecimal threadID = new BigDecimal(Thread.currentThread().getId());
        BigDecimal timeStamp = new BigDecimal(System.currentTimeMillis());
        threadID = threadID.add(timeStamp);
		CommerceSession session = new CommerceSession(this, threadID.intValue());

		// set the customer pk on the session if one is defined.
		session.setCustomerPk(parseCustomerId(customerId));


		// todo:huw
		// finalizeCommerceSessionAtSessionCreated(session);

		// Iterate through the list of create session extensions.
	    Iterator iter = createSessionExtensions.iterator();
	    while (iter.hasNext()) {
	        ICreateSessionExtension createSessionCheck = (ICreateSessionExtension) iter.next();
	        createSessionCheck.createSession(session, customerId, basketId);
	    }

		return session;
	}

	/**
	 * Creates a new basket for the current customer.  Any existing basket is
	 * unaffected.
	 *
	 * @param session
	 */
	public void startNewBasket(ICommerceSession session) {
		session.startNewBasket();
	}

	/**
	 * Releases the commerce session.  This should be called after the response
	 * is written.  The implementation may use it as a trigger to save the session
	 * to persistent store if in a multiple server environment.
	 */
	public void releaseCommerceSession(ICommerceSession session) {
		// remove the session to logging context
		// getSessionLogger().popSession();

	}

	/**
	 * {@inheritDoc}
	 */
	public void finalizeCommerceSession(ICommerceSession session) {
		log.debug(" finalizeCommerceSession() called");
		try {
			// make sure that session will be finalized only once
			if (!session.isFinalized() && session.isExpired() && isOwner(session)) {

				// todo:huw efficient way to maintain session summary
				// finalizer.finalizeSessionOtherThanSessionSummary(session);

				session.setFinalized(true);
				log.debug(" commerce session " + session.getId() + " finalized");
			}
		} catch (Exception e) {
			log.error("Error finalizing session: " + session.getId(), e);
		}
	}

    /**
     * @deprecated As of version 5.3, <code>ICommerceSession</code> cannot be finalized or expired.
     */
	public void finalizeCommerceSessionAtSessionCreated(ICommerceSession session) {
		log.debug(" finalizeCommerceSessionAtSessionCreated() called");
		try {
            // make sure that session will be finalized only once
			if (!session.isFinalized() && isOwner(session)) {
				finalizer.finalizeSessionForSessionSummary(session);
				log.debug(" commerce session " + session.getId() + " finalizeCommerceSessionAtSessionCreated");
			}
		} catch (Exception e) {
			log.error("Error finalizeCommerceSessionAtSessionCreated: " + session.getId(), e);
		}
	}

	/**
	 * Defines if the commerce session was initialized on the app server that it is running on
	 * at this moment.
	 * <p/>
	 * Each commerce session carries a unique server id which is initialized on the first
	 * request sent by a client to the server. If commerce session was serialized to be
	 * transported to a different server, after the serialization it will have a new server unique id.
	 */
	public boolean isOwner(ICommerceSession commerceSession) {
		return true;
	}

    /**
     * {@inheritDoc}
     */
	public void startRequest(ICommerceSession commerceSession) {
		((CommerceSession) commerceSession).startRequest();
	}

    public void setCurrentRequest(HttpServletRequest httpRequest, ICommerceSession commerceSession) {
        commerceSession.setCurrentRequest(httpRequest);
    }

    /**
     * {@inheritDoc}
     */
    public void registerCreateSessionExtension(ICreateSessionExtension createSessionExtension) {
    	createSessionExtensions.add(createSessionExtension);
    }

    /**
     * {@inheritDoc}
     */
    public void registerStartRequestExtension(IStartRequestExtension startRequestExtension) {
    	startRequestExtensions.add(startRequestExtension);
    }

    /**
     * {@inheritDoc}
     */
    public void registerEndRequestExtension(IEndRequestExtension endRequestExtension) {
    	endRequestExtensions.add(endRequestExtension);
    }

    /**
     * {@inheritDoc}
     */
    public void registerLoginExtension(ILoginExtension loginExtension) {
    	loginExtensions.add(loginExtension);
    }

    /**
     * {@inheritDoc}
     */
    public void registerLogoutExtension(ILogoutExtension logoutExtension) {
    	logoutExtensions.add(logoutExtension);
    }

    /**
     * {@inheritDoc}
     */
    public void registerRegisterCustomerExtension(IRegisterCustomerExtension registerCustomerExtension) {
    	registerCustomerExtensions.add(registerCustomerExtension);
    }

    /**
	 * Tries to get a customerPk from an id.  Returns null if it fails.
	 *
	 * @param id
	 * @return primaryKey
	 */
	private IPrimaryKey parseCustomerId(String id) {
        if (id == null){
            return null;
        }
		try {
			return customerHome.getPk(id);
		} catch (Exception e) {
            if (log.isDebugEnabled()) {
			    log.debug("Failed to convert string to customerPk: " + id);
            }
			return null;
		}
	}
}
